﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections;
using System.Reflection;
using System.Windows.Forms;

namespace System
{
    public static class ObjectExt
    {
        private static object[] _TRUE_VALUES = new object[] { 1, "TRUE", "True", "T", "Y", "t", "y", "1", "1.0" };
       
        private static bool? _AppRunning;
        public static void SetAppRunning(this object obj, bool run)
        {
            _AppRunning = run;
        }

        public static bool IsAppRunning(this object value)
        {
            if (_AppRunning == null)
                return Assembly.GetEntryAssembly() != null;
            else
                return _AppRunning.AsBool();
        }

        public static string GetString(this byte[] bytes, Encoding e)
        {
            try
            {
                return e.GetString(bytes);
            }
            catch { }
            return string.Empty;
        }
        public static string GetString(this byte[] bytes)
        {
            return bytes.GetString(Encoding.UTF8);
        }
        #region CONST and Static properties
        
        public const string NEW_LINE_WIN = "\n";
        public const string NEW_LINE_UNIX = "\r\n";
        public static string DECIMAL_SEPARATOR
        {
            get { return System.Globalization.CultureInfo.CurrentUICulture.NumberFormat.CurrencyDecimalSeparator; }
        }
        /// <summary>
        /// Gets or sets default true values for conversion object.AsBool()
        /// </summary>
        public static object[] DEFAULT_TRUE_VALUES
        {
            get { return _TRUE_VALUES; }
            set { _TRUE_VALUES = value; }
        }
        #endregion

        #region Convert to other types
        /// <summary>
        /// If value is null, DBNull returns string.Empty else value.ToString()
        /// </summary>
        /// <param name="value"></param>
        /// <returns></returns>
        public static string AsString(this object value)
        {
            if (value.IsNull())
                return string.Empty;
            return value.ToString();
        }
        /// <summary>
        /// Joins each object from value with separator
        /// </summary>
        /// <param name="value"></param>
        /// <param name="separator"></param>
        /// <returns></returns>
        public static string AsSingleString(this IEnumerable value, string separator = NEW_LINE_WIN)
        {
            string str = string.Empty;
            if (value == null)
                return str;
            foreach (object o in value)
            {
                if (str != string.Empty)
                    str += separator;
                str += o.AsString();
            }
            return str;
        }

        /// <summary>
        /// If can, converts object to Int, else returns defaultValue
        /// </summary>
        /// <param name="value"></param>
        /// <param name="defaultValue"></param>
        /// <returns></returns>
        public static int AsInt(this object value, int defaultValue = 0)
        {
            int i = defaultValue;
            int.TryParse(value.AsString().Trim(), out i);
            return i;
        }
        /// <summary>
        /// If can, converts object to Decimal, else returns defaultValue
        /// </summary>
        /// <param name="value"></param>
        /// <param name="defaultValue"></param>
        /// <returns></returns>
        public static decimal AsDecimal(this object value, decimal defaultValue = decimal.Zero)
        {
            decimal i = defaultValue;
            decimal.TryParse(value.AsString().Trim().Replace(".", DECIMAL_SEPARATOR).Replace(",", DECIMAL_SEPARATOR), out i);
            return i;
        }
        /// <summary>
        /// If can, converts object to Float, else returns defaultValue
        /// </summary>
        /// <param name="value"></param>
        /// <param name="defaultValue"></param>
        /// <returns></returns>
        public static float AsFloat(this object value, float defaultValue = (float)0)
        {
            float i = defaultValue;
            float.TryParse(value.AsString().Trim().Replace(".", DECIMAL_SEPARATOR).Replace(",", DECIMAL_SEPARATOR), out i);
            return i;
        }
        /// <summary>
        /// If can, converts object to Double, else returns defaultValue
        /// </summary>
        /// <param name="value"></param>
        /// <param name="defaultValue"></param>
        /// <returns></returns>
        public static double AsFloat(this object value, double defaultValue = (double)0)
        {
            double i = defaultValue;
            double.TryParse(value.AsString().Trim().Replace(".", DECIMAL_SEPARATOR).Replace(",", DECIMAL_SEPARATOR), out i);
            return i;
        }
        /// <summary>
        /// If can, converts object to Long, else returns defaultValue
        /// </summary>
        /// <param name="value"></param>
        /// <param name="defaultValue"></param>
        /// <returns></returns>
        public static long AsLong(this object value, long defaultValue = (long)0)
        {
            long i = defaultValue;
            long.TryParse(value.AsString().Trim().Replace(".", DECIMAL_SEPARATOR).Replace(",", DECIMAL_SEPARATOR), out i);
            return i;
        }

        /// <summary>
        /// Converts value to bool
        /// </summary>
        /// <param name="value"></param>
        /// <param name="TrueValues">Values for whitch value is true. By default see System.ObjectExt.DEFAULT_TRUE_VALUES</param>
        /// <returns></returns>
        public static bool AsBool(this object value, params object[] TrueValues)
        {
            bool result = false;
            if (bool.TryParse(value.AsString(), out result))
                return result;
            if (TrueValues == null || TrueValues.Length == 0)
            {
                TrueValues = DEFAULT_TRUE_VALUES;
            }
            if (TrueValues != null)
            {
                foreach (object obj in TrueValues)
                {
                    if (value.IsTheSame(obj))
                        return true;
                }
            }
            return false;
        }
        /// <summary>
        /// Converts value to DateTime or returns Default value if value cannot be converted
        /// </summary>
        /// <param name="value"></param>
        /// <param name="defaultValue"></param>
        /// <returns></returns>
        public static DateTime AsDateTime(this object value, DateTime defaultValue)
        {
            DateTime d = defaultValue;
            DateTime.TryParse(value.AsString(), out d);
            return d;
        }
        /// <summary>
        /// Converts value to DateTime or returns DateTime.MinValue if it can not be converted
        /// </summary>
        /// <param name="value"></param>
        /// <returns></returns>
        public static DateTime AsDateTime(this object value)
        {
            return value.AsDateTime(DateTime.MinValue);
        }

        /// <summary>
        /// Converts value to Enum or returns default Enum value
        /// </summary>
        /// <typeparam name="T">Enum type</typeparam>
        /// <param name="value"></param>
        /// <returns></returns>
        public static T AsEnum<T>(this object value) where T : struct
        {
            T t = default(T);
            Enum.TryParse<T>(value.AsString(), out t);
            return t;

        }

        #endregion

        #region Compare, null or not
        public static bool IsTheSame(this object obj1, object obj2, IComparer comparer = null)
        {
            if (comparer != null)
                return comparer.Compare(obj1, obj2) == 0;

            if (obj1.IsNull() && obj2.IsNull())
                return true;
            if (obj1.IsNull() || obj2.IsNull())
                return false;
            if (obj1.GetType() != obj2.GetType())
                return false;
            if (obj1 is IComparable)
                return (obj1 as IComparable).CompareTo(obj2) == 0;

            return obj1.Equals(obj2);
        }
        /// <summary>
        /// Checks if object is Null, BDNull or, if checkEmpy = true, empty array, collection or blank string
        /// </summary>
        /// <param name="obj">Object to check</param>
        /// <param name="checkEmpty">Check empty array</param>
        /// <returns></returns>
        public static bool IsNull(this object obj, bool checkEmpty = true)
        {
            if (obj == null || obj == DBNull.Value)
                return true;
            if (checkEmpty && obj is Array)
            {
                if ((obj as Array).Length == 0)
                    return true;
            }

            if (obj is ICollection)
                if ((obj as ICollection).Count == 0)
                    return true;

            if (checkEmpty && obj is string)
                return string.IsNullOrEmpty(obj.ToString().Trim());
            return false;
        }
        /// <summary>
        /// Checks if object is not Null, BDNull and, if checkEmpy = true, not empty array, collection nor blank string
        /// </summary>
        /// <param name="obj"></param>
        /// <param name="checkEmpty"></param>
        /// <returns></returns>
        public static bool IsNotNull(this object obj, bool checkEmpty = true)
        {
            return !obj.IsNull(checkEmpty);
        }
        #endregion

        #region Reflection Fields, Properties, Methods
        /// <summary>
        /// Gets PropertyInfo
        /// </summary>
        /// <param name="type"></param>
        /// <param name="name"></param>
        /// <param name="flags"></param>
        /// <returns></returns>
        public static PropertyInfo GetSingleProperty(this Type type, string name, BindingFlags flags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.IgnoreCase | BindingFlags.FlattenHierarchy)
        {
            PropertyInfo pi = null;

            if (type != null)
            {
                PropertyInfo[] pis = type.GetProperties(flags);
                try
                {
                    bool ignoreCase = ((flags & BindingFlags.IgnoreCase) == BindingFlags.IgnoreCase);
                    foreach (PropertyInfo p in pis)
                    {
                        if ((p.Name == name) || (ignoreCase && (p.Name.ToUpper() == name.ToUpper())))
                        {
                            pi = p;
                            if (p.DeclaringType.Equals(type))
                            {
                                break;
                            }
                        }
                    }
                    if (pi == null)
                    {
                        Type t = type;

                        while (t != null)
                        {
                            t = t.BaseType;
                            pi = GetSingleProperty(t, name, flags);
                            if (pi != null)
                                break;
                        }
                    }
                }
                catch
                {
                }
            }
            return pi;
        }
        /// <summary>
        /// Gets FieldInfo
        /// </summary>
        /// <param name="type"></param>
        /// <param name="name"></param>
        /// <param name="flags"></param>
        /// <returns></returns>
        public static FieldInfo GetSingleField(this Type type, string name, BindingFlags flags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.IgnoreCase | BindingFlags.FlattenHierarchy)
        {
            FieldInfo pi = null;

            if (type != null)
            {
                FieldInfo[] pis = type.GetFields(flags);
                try
                {
                    bool ignoreCase = ((flags & BindingFlags.IgnoreCase) == BindingFlags.IgnoreCase);
                    foreach (FieldInfo p in pis)
                    {
                        if ((p.Name == name) || (ignoreCase && (p.Name.ToUpper() == name.ToUpper())))
                        {
                            pi = p;
                            if (p.DeclaringType.Equals(type))
                            {
                                break;
                            }
                        }
                    }
                    if (pi == null)
                    {
                        Type t = type;

                        while (t != null)
                        {
                            t = t.BaseType;
                            pi = GetSingleField(t, name, flags);
                            if (pi != null)
                                break;
                        }
                    }
                }
                catch
                {
                }
            }
            return pi;
        }
        /// <summary>
        /// Gets MethodInfo
        /// </summary>
        /// <param name="type"></param>
        /// <param name="name"></param>
        /// <param name="parameters"></param>
        /// <param name="flags"></param>
        /// <returns></returns>
        public static MethodInfo GetSingleMethod(this Type type, string name, object[] parameters = null, BindingFlags flags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.IgnoreCase | BindingFlags.FlattenHierarchy)
        {
            MethodInfo pi = null;

            if (type != null)
            {
                MethodInfo[] pis = type.GetMethods(flags);
                Type pt = null;
                bool nullable = true;
                try
                {
                    bool ignoreCase = ((flags & BindingFlags.IgnoreCase) == BindingFlags.IgnoreCase);
                    foreach (MethodInfo p in pis)
                    {
                        if ((p.Name == name) || (ignoreCase && (p.Name.ToUpper() == name.ToUpper())))
                        {
                            pi = p;
                            if (p.DeclaringType.Equals(type))
                            {
                                bool isThis = true;
                                if (parameters != null)
                                {
                                    ParameterInfo[] par = pi.GetParameters();
                                    if (par.Length == parameters.Length)
                                    {
                                        for (int i = 0; i < par.Length; i++)
                                        {
                                            pt = par[i].ParameterType;
                                            nullable = false;
                                            if (!pt.IsValueType) 
                                                nullable = true; // ref-type
                                            if (Nullable.GetUnderlyingType(type) != null) 
                                                nullable = true; // Nullable<T>
                                            if (parameters[i] == null )
                                            {
                                                if (!nullable)
                                                {
                                                    isThis = false;
                                                    break;
                                                }
                                            }
                                            else
                                            {
                                                if (!pt.IsAssignableFrom(parameters[i].GetType()))
                                                {
                                                    isThis = false;
                                                    break;
                                                }
                                            }
                                        }
                                    }
                                }
                                if (isThis)
                                    break;
                            }
                        }
                    }
                    if (pi == null)
                    {
                        Type t = type;

                        while (t != null)
                        {
                            t = t.BaseType;
                            pi = GetSingleMethod(t, name, parameters, flags);
                            if (pi != null)
                                break;
                        }
                    }
                }
                catch
                {
                }
            }
            return pi;
        }

        /// <summary>
        /// Gets property value if target contains this property, else returns null
        /// </summary>
        /// <param name="target"></param>
        /// <param name="PropertyName"></param>
        /// <returns></returns>
        public static object GetPropertyValue(this object target, string PropertyName)
        {
            object result = null;
            try
            {
                PropertyInfo p = target.GetType().GetSingleProperty(PropertyName);
                if (p != null)
                {
                    result = p.GetValue(target, null);
                }
            }
            catch { }
            return result;
        }
        /// <summary>
        /// Sets property value for target
        /// </summary>
        /// <param name="target"></param>
        /// <param name="PropertyName"></param>
        /// <param name="value">True if operation succeeded, False otherwice</param>
        /// <returns></returns>
        public static bool SetPropertyValue(this object target, string PropertyName, object value)
        {
            bool result = false;
            try
            {
                PropertyInfo p = target.GetType().GetSingleProperty(PropertyName);
                if (p != null)
                {
                    p.SetValue(target, value, null);
                    result = true;
                }
            }
            catch { }
            return result;
        }
        /// <summary>
        /// Gets Field value if target contains this field, else returns null
        /// </summary>
        /// <param name="target"></param>
        /// <param name="FieldName"></param>
        /// <returns></returns>
        public static object GetFieldValue(this object target, string FieldName)
        {
            object result = null;
            try
            {
                FieldInfo p = target.GetType().GetSingleField(FieldName);
                if (p != null)
                    result = p.GetValue(target);
            }
            catch { }
            return result;
        }
        /// <summary>
        /// Sets field value for target
        /// </summary>
        /// <param name="target"></param>
        /// <param name="FieldName"></param>
        /// <param name="value">True if operation succeeded, False otherwice</param>
        /// <returns></returns>
        public static bool SetFieldValue(this object target, string FieldName, object value)
        {
            bool result = false;
            try
            {
                FieldInfo p = target.GetType().GetSingleField(FieldName);
                if (p != null)
                {
                    p.SetValue(target, value);
                    result = true;
                }
            }
            catch { }
            return result;
        }
        /// <summary>
        /// Runs method on target, and returns result. If there was exception or target does not have such method returns null
        /// </summary>
        /// <param name="target"></param>
        /// <param name="MethodName"></param>
        /// <param name="parameters"></param>
        /// <returns></returns>
        public static object RunMethod(this object target, string MethodName, object[] parameters = null)
        {
            object result = null;
            try
            {
                MethodInfo mi = target.GetType().GetSingleMethod(MethodName, parameters);
                if (mi != null)
                {
                    result = mi.Invoke(target, parameters);
                }
            }
            catch { }
            return result;
        }


        #endregion
    }
}
